import { TreeNode } from '../model'
import { Consumer, Function, Predicate } from '../util'
import { ArrayTool } from './ArrayTool'

// noinspection JSUnusedGlobalSymbols
/**
 * TreeTool
 * @author 冰凝
 * @date 2022-09-19 09:45:27
 **/
export class TreeTool {

    /**
     * 构建树结构
     * @param treeList 展开的节点列表
     * @param filterRoot 过滤根节点的策略
     * @param idKey ID 属性名
     * @param pidKey parentId 属性名
     * @param childrenKey children 列表 属性名
     * @param leaveEmptyChildren 是否保留空的子节点, 默认不保留
     */
    public static build<
        E extends object,
        I extends keyof E,
        P extends keyof E,
        C extends keyof E
    >(
        treeList: Array<E>,
        filterRoot: Predicate<E>,
        idKey: I,
        pidKey: P,
        childrenKey: C,
        leaveEmptyChildren: boolean = false,
    ): Array<E> {
        if (ArrayTool.isEmpty(treeList)) {
            return []
        }

        const mapping = ArrayTool.toMap(treeList, i => i[idKey])

        for (const node of treeList) {
            const pid = node[pidKey]
            if (pid === undefined) {
                continue
            }
            const pNode = mapping.get(pid as any)
            if (!pNode) {
                continue
            }

            if (ArrayTool.isEmpty(pNode[childrenKey] as Array<any>)) {
                pNode[childrenKey] = [] as any
            }
            (pNode[childrenKey] as Array<E>).push(node)
        }

        let root: Array<E> = treeList.filter(filterRoot)

        // 是否保留空节点
        if (!leaveEmptyChildren) {
            root = this.map(
                root,
                i => {
                    if (ArrayTool.isEmpty(i[childrenKey] as any)) {
                        delete i[childrenKey]
                    }
                    return i
                },
                childrenKey)
        }

        return root
    }

    /**
     * 树结构遍历
     * @param treeArr 源树结构数组
     * @param consumer 需要对节点元素执行操作的映射器
     * @param childrenKey Children 属性名, 默认 children
     * @returns 映射器返回的结果的数组
     */
    public static each<T, R>(
        treeArr: Array<T>,
        consumer: Consumer<T>,
        childrenKey: keyof T = 'children' as keyof T,
    ): void {
        if (ArrayTool.isEmpty(treeArr)) {
            return
        }

        for (const i of treeArr) {
            if (ArrayTool.isNotEmpty(i[childrenKey] as any)) {
                this.each(i[childrenKey] as any, consumer, childrenKey)
            }
            consumer(i)
        }
    }

    /**
     * 树映射: 遍历并转换每个节点
     * @see each
     */
    public static map<T, R = T>(
        treeArr: Array<T>,
        map: Function<T, R> = i => i as any,
        childrenKey: keyof T = 'children' as keyof T,
    ): Array<R> {
        if (ArrayTool.isEmpty(treeArr)) {
            return []
        }

        return treeArr.map(i => {
            if (ArrayTool.isNotEmpty(i[childrenKey] as any)) {
                i[childrenKey] = this.map(i[childrenKey] as any, map, childrenKey) as any
            }
            return map(i)
        })
    }

    /**
     * 根据指定子节点, 递归寻找所在树中 从根节点到指定节点路径上所有节点
     * @param data 树
     * @param currentNode 目标节点
     * @param id  判断是否相等的属性名, 默认 id
     * @param child 子节点属性名, 默认 children
     */
    public static path<T>(data: T | T[], currentNode: T, id = 'id', child = 'children') {
        const pathArr: T[] = []
        let find = false

        const recursionTree = (treeData: T | T[]) => {
            if (find) {
                return
            }
            // 数组
            if (Array.isArray(treeData)) {
                for (const treeDataItem of treeData) {
                    recursionTree(treeDataItem)
                    if (find) {
                        return
                    }
                }
                return
            }

            // 对象
            if (treeData[id] === currentNode[id]) {
                find = true
                pathArr.push(treeData as any)
                return
            }
            // 树节点
            const childList = treeData[child]
            if (Array.isArray(childList) && childList.length > 0) {
                for (const c of childList) {
                    // 深度 +1
                    pathArr.push(treeData)
                    recursionTree(c)
                    if (find) {
                        return
                    } else {
                        pathArr.pop()
                    }
                }
            }
        }
        recursionTree(data)

        return pathArr
    }

    /**
     * 经典树形列表展开
     * @param treeList
     * @param keyMapping
     * @param valueMapping
     */
    public static expandMapping<
        E extends TreeNode<E, K>,
        V = E,
        K extends PropertyKey = string
    >(
        treeList: Array<E>,
        keyMapping: (node: E) => K,
        valueMapping: (node: E) => V = i => i as any,
    ): Record<K, V> {
        const end: Record<K, V> = <Record<K, V>>{}
        this.each(treeList, i => end[keyMapping(i)] = valueMapping(i))
        return end
    }

    /**
     * 树过滤
     * @param treeList 源树形列表
     * @param predicate 判断是否过滤, 返回 false 不保留 {@link Array.filter}
     * @param leaveEmpty 是否保留空的 children 默认 false;
     */
    public static filter<
        E extends TreeNode<E, K>,
        K extends PropertyKey
    >(
        treeList: Array<E>,
        predicate: Predicate<E>,
        leaveEmpty: boolean = false,
    ): Array<E> {
        if (ArrayTool.isEmpty(treeList)) {
            return treeList
        }
        for (let i = 0; i < treeList.length; i++) {
            const node = treeList[i]!
            if (ArrayTool.isNotEmpty(node.children)) {
                node.children = this.filter(node.children!, predicate, leaveEmpty)
            }

            if (!leaveEmpty && ArrayTool.isEmpty(node.children)) {
                delete node.children
            }
        }

        return treeList.filter(predicate)
    }

}
